home *** CD-ROM | disk | FTP | other *** search
/ X User Tools / X User Tools (O'Reilly and Associates)(1994).ISO / sources / libxpm / libxpm34.gz / libxpm34 / xpm-3.4 / lib / parse.c < prev    next >
C/C++ Source or Header  |  1994-03-14  |  17KB  |  658 lines

  1. /* Copyright 1989-94 GROUPE BULL -- See license conditions in file COPYRIGHT */
  2. /*****************************************************************************\
  3. * parse.c:                                                                    *
  4. *                                                                             *
  5. *  XPM library                                                                *
  6. *  Parse an XPM file or array and store the found informations                *
  7. *  in the given XpmImage structure.                                           *
  8. *                                                                             *
  9. *  Developed by Arnaud Le Hors                                                *
  10. \*****************************************************************************/
  11.  
  12.  
  13. #include "xpmP.h"
  14. #ifdef VMS
  15. #include "sys$library:ctype.h"
  16. #else
  17. #include <ctype.h>
  18. #endif
  19.  
  20. LFUNC(ParseValues, int, (xpmData *data, unsigned int *width,
  21.              unsigned int *height, unsigned int *ncolors,
  22.              unsigned int *cpp, unsigned int *x_hotspot,
  23.              unsigned int *y_hotspot, unsigned int *hotspot,
  24.              unsigned int *extensions));
  25.  
  26. LFUNC(ParseColors, int, (xpmData *data, unsigned int ncolors, unsigned int cpp,
  27.              XpmColor **colorTablePtr, xpmHashTable *hashtable));
  28.  
  29. LFUNC(ParsePixels, int, (xpmData *data, unsigned int width,
  30.              unsigned int height, unsigned int ncolors,
  31.              unsigned int cpp, XpmColor *colorTable,
  32.              xpmHashTable *hashtable, unsigned int **pixels));
  33.  
  34. LFUNC(ParseExtensions, int, (xpmData *data, XpmExtension **extensions,
  35.                  unsigned int *nextensions));
  36.  
  37. char *xpmColorKeys[] = {
  38.     "s",                /* key #1: symbol */
  39.     "m",                /* key #2: mono visual */
  40.     "g4",                /* key #3: 4 grays visual */
  41.     "g",                /* key #4: gray visual */
  42.     "c",                /* key #5: color visual */
  43. };
  44.  
  45.  
  46. /* function call in case of error, frees only locally allocated variables */
  47. #undef RETURN
  48. #define RETURN(status) \
  49. { \
  50.     if (colorTable) xpmFreeColorTable(colorTable, ncolors); \
  51.     if (pixelindex) XpmFree(pixelindex); \
  52.     if (hints_cmt)  XpmFree(hints_cmt); \
  53.     if (colors_cmt) XpmFree(colors_cmt); \
  54.     if (pixels_cmt) XpmFree(pixels_cmt); \
  55.     return(status); \
  56. }
  57.  
  58. /*
  59.  * This function parses an Xpm file or data and store the found informations
  60.  * in an an XpmImage structure which is returned.
  61.  */
  62. int
  63. xpmParseData(data, image, info)
  64.     xpmData *data;
  65.     XpmImage *image;
  66.     XpmInfo *info;
  67. {
  68.     /* variables to return */
  69.     unsigned int width, height, ncolors, cpp;
  70.     unsigned int x_hotspot, y_hotspot, hotspot = 0, extensions = 0;
  71.     XpmColor *colorTable = NULL;
  72.     unsigned int *pixelindex = NULL;
  73.     char *hints_cmt = NULL;
  74.     char *colors_cmt = NULL;
  75.     char *pixels_cmt = NULL;
  76.  
  77.     unsigned int cmts;
  78.     int ErrorStatus;
  79.     xpmHashTable hashtable;
  80.  
  81.     cmts = info && (info->valuemask & XpmReturnComments);
  82.  
  83.     /*
  84.      * parse the header
  85.      */
  86.     ErrorStatus = xpmParseHeader(data);
  87.     if (ErrorStatus != XpmSuccess)
  88.     return (ErrorStatus);
  89.  
  90.     /*
  91.      * read values
  92.      */
  93.     ErrorStatus = ParseValues(data, &width, &height, &ncolors, &cpp,
  94.                 &x_hotspot, &y_hotspot, &hotspot, &extensions);
  95.     if (ErrorStatus != XpmSuccess)
  96.     return (ErrorStatus);
  97.  
  98.     /*
  99.      * store the hints comment line
  100.      */
  101.     if (cmts)
  102.     xpmGetCmt(data, &hints_cmt);
  103.  
  104.     /*
  105.      * init the hastable
  106.      */
  107.     if (USE_HASHTABLE) {
  108.     ErrorStatus = xpmHashTableInit(&hashtable);
  109.     if (ErrorStatus != XpmSuccess)
  110.         return (ErrorStatus);
  111.     }
  112.  
  113.     /*
  114.      * read colors
  115.      */
  116.     ErrorStatus = ParseColors(data, ncolors, cpp, &colorTable, &hashtable);
  117.     if (ErrorStatus != XpmSuccess)
  118.     RETURN(ErrorStatus);
  119.  
  120.     /*
  121.      * store the colors comment line
  122.      */
  123.     if (cmts)
  124.     xpmGetCmt(data, &colors_cmt);
  125.  
  126.     /*
  127.      * read pixels and index them on color number
  128.      */
  129.     ErrorStatus = ParsePixels(data, width, height, ncolors, cpp, colorTable,
  130.                   &hashtable, &pixelindex);
  131.  
  132.     /*
  133.      * free the hastable
  134.      */
  135.     if (USE_HASHTABLE)
  136.     xpmHashTableFree(&hashtable);
  137.  
  138.     if (ErrorStatus != XpmSuccess)
  139.     RETURN(ErrorStatus);
  140.  
  141.     /*
  142.      * store the pixels comment line
  143.      */
  144.     if (cmts)
  145.     xpmGetCmt(data, &pixels_cmt);
  146.  
  147.     /*
  148.      * parse extensions
  149.      */
  150.     if (info && (info->valuemask & XpmReturnExtensions))
  151.     if (extensions) {
  152.         ErrorStatus = ParseExtensions(data, &info->extensions,
  153.                       &info->nextensions);
  154.         if (ErrorStatus != XpmSuccess)
  155.         RETURN(ErrorStatus);
  156.     } else {
  157.         info->extensions = NULL;
  158.         info->nextensions = 0;
  159.     }
  160.  
  161.     /*
  162.      * store found informations in the XpmImage structure
  163.      */
  164.     image->width = width;
  165.     image->height = height;
  166.     image->cpp = cpp;
  167.     image->ncolors = ncolors;
  168.     image->colorTable = colorTable;
  169.     image->data = pixelindex;
  170.  
  171.     if (info) {
  172.     if (cmts) {
  173.         info->hints_cmt = hints_cmt;
  174.         info->colors_cmt = colors_cmt;
  175.         info->pixels_cmt = pixels_cmt;
  176.     }
  177.     if (hotspot) {
  178.         info->x_hotspot = x_hotspot;
  179.         info->y_hotspot = y_hotspot;
  180.         info->valuemask |= XpmHotspot;
  181.     }
  182.     }
  183.     return (XpmSuccess);
  184. }
  185.  
  186. static int
  187. ParseValues(data, width, height, ncolors, cpp,
  188.         x_hotspot, y_hotspot, hotspot, extensions)
  189.     xpmData *data;
  190.     unsigned int *width, *height, *ncolors, *cpp;
  191.     unsigned int *x_hotspot, *y_hotspot, *hotspot;
  192.     unsigned int *extensions;
  193. {
  194.     unsigned int l;
  195.     char buf[BUFSIZ];
  196.  
  197.     if (!data->format) {        /* XPM 2 or 3 */
  198.  
  199.     /*
  200.      * read values: width, height, ncolors, chars_per_pixel
  201.      */
  202.     if (!(xpmNextUI(data, width) && xpmNextUI(data, height)
  203.           && xpmNextUI(data, ncolors) && xpmNextUI(data, cpp)))
  204.         return (XpmFileInvalid);
  205.  
  206.     /*
  207.      * read optional information (hotspot and/or XPMEXT) if any
  208.      */
  209.     l = xpmNextWord(data, buf, BUFSIZ);
  210.     if (l) {
  211.         *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
  212.         if (*extensions)
  213.         *hotspot = (xpmNextUI(data, x_hotspot)
  214.                 && xpmNextUI(data, y_hotspot));
  215.         else {
  216.         *hotspot = (atoui(buf, l, x_hotspot)
  217.                 && xpmNextUI(data, y_hotspot));
  218.         l = xpmNextWord(data, buf, BUFSIZ);
  219.         *extensions = (l == 6 && !strncmp("XPMEXT", buf, 6));
  220.         }
  221.     }
  222.     } else {
  223.  
  224.     /*
  225.      * XPM 1 file read values: width, height, ncolors, chars_per_pixel
  226.      */
  227.     int i;
  228.     char *ptr;
  229.  
  230.     for (i = 0; i < 4; i++) {
  231.         l = xpmNextWord(data, buf, BUFSIZ);
  232.         if (l != 7 || strncmp("#define", buf, 7))
  233.         return (XpmFileInvalid);
  234.         l = xpmNextWord(data, buf, BUFSIZ);
  235.         if (!l)
  236.         return (XpmFileInvalid);
  237.         ptr = index(buf, '_');
  238.         if (!ptr)
  239.         return (XpmFileInvalid);
  240.         switch (l - (ptr - buf)) {
  241.         case 6:
  242.         if (!strncmp("_width", ptr, 6) && !xpmNextUI(data, width))
  243.             return (XpmFileInvalid);
  244.         break;
  245.         case 7:
  246.         if (!strncmp("_height", ptr, 7) && !xpmNextUI(data, height))
  247.             return (XpmFileInvalid);
  248.         break;
  249.         case 8:
  250.         if (!strncmp("_ncolors", ptr, 8) && !xpmNextUI(data, ncolors))
  251.             return (XpmFileInvalid);
  252.         break;
  253.         case 16:
  254.         if (!strncmp("_chars_per_pixel", ptr, 16)
  255.             && !xpmNextUI(data, cpp))
  256.             return (XpmFileInvalid);
  257.         break;
  258.         default:
  259.         return (XpmFileInvalid);
  260.         }
  261.         /* skip the end of line */
  262.         xpmNextString(data);
  263.     }
  264.     *hotspot = 0;
  265.     *extensions = 0;
  266.     }
  267.     return (XpmSuccess);
  268. }
  269.  
  270. static int
  271. ParseColors(data, ncolors, cpp, colorTablePtr, hashtable)
  272.     xpmData *data;
  273.     unsigned int ncolors;
  274.     unsigned int cpp;
  275.     XpmColor **colorTablePtr;
  276.     xpmHashTable *hashtable;
  277. {
  278.     unsigned int key, l, a, b;
  279.     unsigned int curkey;        /* current color key */
  280.     unsigned int lastwaskey;        /* key read */
  281.     char buf[BUFSIZ];
  282.     char curbuf[BUFSIZ];        /* current buffer */
  283.     char **sptr, *s;
  284.     XpmColor *color;
  285.     XpmColor *colorTable;
  286.     char **defaults;
  287.     int ErrorStatus;
  288.  
  289.     colorTable = (XpmColor *) XpmCalloc(ncolors, sizeof(XpmColor));
  290.     if (!colorTable)
  291.     return (XpmNoMemory);
  292.  
  293.     if (!data->format) {        /* XPM 2 or 3 */
  294.     for (a = 0, color = colorTable; a < ncolors; a++, color++) {
  295.         xpmNextString(data);    /* skip the line */
  296.  
  297.         /*
  298.          * read pixel value
  299.          */
  300.         color->string = (char *) XpmMalloc(cpp + 1);
  301.         if (!color->string) {
  302.         xpmFreeColorTable(colorTable, ncolors);
  303.         return (XpmNoMemory);
  304.         }
  305.         for (b = 0, s = color->string; b < cpp; b++, s++)
  306.         *s = xpmGetC(data);
  307.         *s = '\0';
  308.  
  309.         /*
  310.          * store the string in the hashtable with its color index number
  311.          */
  312.         if (USE_HASHTABLE) {
  313.         ErrorStatus =
  314.             xpmHashIntern(hashtable, color->string, HashAtomData(a));
  315.         if (ErrorStatus != XpmSuccess) {
  316.             xpmFreeColorTable(colorTable, ncolors);
  317.             return (ErrorStatus);
  318.         }
  319.         }
  320.  
  321.         /*
  322.          * read color keys and values
  323.          */
  324.         defaults = (char **) color;
  325.         curkey = 0;
  326.         lastwaskey = 0;
  327.         *curbuf = '\0';        /* init curbuf */
  328.         while (l = xpmNextWord(data, buf, BUFSIZ)) {
  329.         if (!lastwaskey) {
  330.             for (key = 0, sptr = xpmColorKeys; key < NKEYS; key++,
  331.              sptr++)
  332.             if ((strlen(*sptr) == l) && (!strncmp(*sptr, buf, l)))
  333.                 break;
  334.         }
  335.         if (!lastwaskey && key < NKEYS) {    /* open new key */
  336.             if (curkey) {    /* flush string */
  337.             s = (char *) XpmMalloc(strlen(curbuf) + 1);
  338.             if (!s) {
  339.                 xpmFreeColorTable(colorTable, ncolors);
  340.                 return (XpmNoMemory);
  341.             }
  342.             defaults[curkey] = s;
  343.             strcpy(s, curbuf);
  344.             }
  345.             curkey = key + 1;    /* set new key  */
  346.             *curbuf = '\0';    /* reset curbuf */
  347.             lastwaskey = 1;
  348.         } else {
  349.             if (!curkey) {    /* key without value */
  350.             xpmFreeColorTable(colorTable, ncolors);
  351.             return (XpmFileInvalid);
  352.             }
  353.             if (!lastwaskey)
  354.             strcat(curbuf, " ");    /* append space */
  355.             buf[l] = '\0';
  356.             strcat(curbuf, buf);/* append buf */
  357.             lastwaskey = 0;
  358.         }
  359.         }
  360.         if (!curkey) {        /* key without value */
  361.         xpmFreeColorTable(colorTable, ncolors);
  362.         return (XpmFileInvalid);
  363.         }
  364.         s = defaults[curkey] = (char *) XpmMalloc(strlen(curbuf) + 1);
  365.         if (!s) {
  366.         xpmFreeColorTable(colorTable, ncolors);
  367.         return (XpmNoMemory);
  368.         }
  369.         strcpy(s, curbuf);
  370.     }
  371.     } else {                /* XPM 1 */
  372.     /* get to the beginning of the first string */
  373.     data->Bos = '"';
  374.     data->Eos = '\0';
  375.     xpmNextString(data);
  376.     data->Eos = '"';
  377.     for (a = 0, color = colorTable; a < ncolors; a++, color++) {
  378.  
  379.         /*
  380.          * read pixel value
  381.          */
  382.         color->string = (char *) XpmMalloc(cpp + 1);
  383.         if (!color->string) {
  384.         xpmFreeColorTable(colorTable, ncolors);
  385.         return (XpmNoMemory);
  386.         }
  387.         for (b = 0, s = color->string; b < cpp; b++, s++)
  388.         *s = xpmGetC(data);
  389.         *s = '\0';
  390.  
  391.         /*
  392.          * store the string in the hashtable with its color index number
  393.          */
  394.         if (USE_HASHTABLE) {
  395.         ErrorStatus =
  396.             xpmHashIntern(hashtable, color->string, HashAtomData(a));
  397.         if (ErrorStatus != XpmSuccess) {
  398.             xpmFreeColorTable(colorTable, ncolors);
  399.             return (ErrorStatus);
  400.         }
  401.         }
  402.  
  403.         /*
  404.          * read color values
  405.          */
  406.         xpmNextString(data);    /* get to the next string */
  407.         *curbuf = '\0';        /* init curbuf */
  408.         while (l = xpmNextWord(data, buf, BUFSIZ)) {
  409.         if (*curbuf != '\0')
  410.             strcat(curbuf, " ");/* append space */
  411.         buf[l] = '\0';
  412.         strcat(curbuf, buf);    /* append buf */
  413.         }
  414.         s = (char *) XpmMalloc(strlen(curbuf) + 1);
  415.         if (!s) {
  416.         xpmFreeColorTable(colorTable, ncolors);
  417.         return (XpmNoMemory);
  418.         }
  419.         strcpy(s, curbuf);
  420.         color->c_color = s;
  421.         *curbuf = '\0';        /* reset curbuf */
  422.         if (a < ncolors - 1)
  423.         xpmNextString(data);    /* get to the next string */
  424.     }
  425.     }
  426.     *colorTablePtr = colorTable;
  427.     return (XpmSuccess);
  428. }
  429.  
  430. static int
  431. ParsePixels(data, width, height, ncolors, cpp, colorTable, hashtable, pixels)
  432.     xpmData *data;
  433.     unsigned int width;
  434.     unsigned int height;
  435.     unsigned int ncolors;
  436.     unsigned int cpp;
  437.     XpmColor *colorTable;
  438.     xpmHashTable *hashtable;
  439.     unsigned int **pixels;
  440. {
  441.     unsigned int *iptr, *iptr2;
  442.     unsigned int a, x, y;
  443.  
  444.     iptr2 = (unsigned int *) XpmMalloc(sizeof(unsigned int) * width * height);
  445.     if (!iptr2)
  446.     return (XpmNoMemory);
  447.  
  448.     iptr = iptr2;
  449.  
  450.     switch (cpp) {
  451.  
  452.     case (1):                /* Optimize for single character
  453.                      * colors */
  454.     {
  455.         unsigned short colidx[256];
  456.  
  457.         bzero(colidx, 256 * sizeof(short));
  458.         for (a = 0; a < ncolors; a++)
  459.         colidx[colorTable[a].string[0]] = a + 1;
  460.  
  461.         for (y = 0; y < height; y++) {
  462.         xpmNextString(data);
  463.         for (x = 0; x < width; x++, iptr++) {
  464.             int idx = colidx[xpmGetC(data)];
  465.  
  466.             if (idx != 0)
  467.             *iptr = idx - 1;
  468.             else {
  469.             XpmFree(iptr2);
  470.             return (XpmFileInvalid);
  471.             }
  472.         }
  473.         }
  474.     }
  475.     break;
  476.  
  477.     case (2):                /* Optimize for double character
  478.                      * colors */
  479.     {
  480.         unsigned short cidx[256][256];
  481.  
  482.         bzero(cidx, 256 * 256 * sizeof(short));
  483.         for (a = 0; a < ncolors; a++)
  484.         cidx[colorTable[a].string[0]][colorTable[a].string[1]] = a + 1;
  485.  
  486.         for (y = 0; y < height; y++) {
  487.         xpmNextString(data);
  488.         for (x = 0; x < width; x++, iptr++) {
  489.             int cc1 = xpmGetC(data);
  490.             int idx = cidx[cc1][xpmGetC(data)];
  491.  
  492.             if (idx != 0)
  493.             *iptr = idx - 1;
  494.             else {
  495.             XpmFree(iptr2);
  496.             return (XpmFileInvalid);
  497.             }
  498.         }
  499.         }
  500.     }
  501.     break;
  502.  
  503.     default:                /* Non-optimized case of long color
  504.                      * names */
  505.     {
  506.         char *s;
  507.         char buf[BUFSIZ];
  508.  
  509.         buf[cpp] = '\0';
  510.         if (USE_HASHTABLE) {
  511.         xpmHashAtom *slot;
  512.  
  513.         for (y = 0; y < height; y++) {
  514.             xpmNextString(data);
  515.             for (x = 0; x < width; x++, iptr++) {
  516.             for (a = 0, s = buf; a < cpp; a++, s++)
  517.                 *s = xpmGetC(data);
  518.             slot = xpmHashSlot(hashtable, buf);
  519.             if (!*slot) {    /* no color matches */
  520.                 XpmFree(iptr2);
  521.                 return (XpmFileInvalid);
  522.             }
  523.             *iptr = HashColorIndex(slot);
  524.             }
  525.         }
  526.         } else {
  527.         for (y = 0; y < height; y++) {
  528.             xpmNextString(data);
  529.             for (x = 0; x < width; x++, iptr++) {
  530.             for (a = 0, s = buf; a < cpp; a++, s++)
  531.                 *s = xpmGetC(data);
  532.             for (a = 0; a < ncolors; a++)
  533.                 if (!strcmp(colorTable[a].string, buf))
  534.                 break;
  535.             if (a == ncolors) {    /* no color matches */
  536.                 XpmFree(iptr2);
  537.                 return (XpmFileInvalid);
  538.             }
  539.             *iptr = a;
  540.             }
  541.         }
  542.         }
  543.     }
  544.     break;
  545.     }
  546.     *pixels = iptr2;
  547.     return (XpmSuccess);
  548. }
  549.  
  550. static int
  551. ParseExtensions(data, extensions, nextensions)
  552.     xpmData *data;
  553.     XpmExtension **extensions;
  554.     unsigned int *nextensions;
  555. {
  556.     XpmExtension *exts = NULL, *ext;
  557.     unsigned int num = 0;
  558.     unsigned int nlines, a, l, notstart, notend = 0;
  559.     int status;
  560.     char *string, *s, *s2, **sp;
  561.  
  562.     xpmNextString(data);
  563.     exts = (XpmExtension *) XpmMalloc(sizeof(XpmExtension));
  564.     /* get the whole string */
  565.     status = xpmGetString(data, &string, &l);
  566.     if (status != XpmSuccess) {
  567.     XpmFree(exts);
  568.     return (status);
  569.     }
  570.     /* look for the key word XPMEXT, skip lines before this */
  571.     while ((notstart = strncmp("XPMEXT", string, 6))
  572.        && (notend = strncmp("XPMENDEXT", string, 9))) {
  573.     XpmFree(string);
  574.     xpmNextString(data);
  575.     status = xpmGetString(data, &string, &l);
  576.     if (status != XpmSuccess) {
  577.         XpmFree(exts);
  578.         return (status);
  579.     }
  580.     }
  581.     if (!notstart)
  582.     notend = strncmp("XPMENDEXT", string, 9);
  583.     while (!notstart && notend) {
  584.     /* there starts an extension */
  585.     ext = (XpmExtension *)
  586.         XpmRealloc(exts, (num + 1) * sizeof(XpmExtension));
  587.     if (!ext) {
  588.         XpmFree(string);
  589.         XpmFreeExtensions(exts, num);
  590.         return (XpmNoMemory);
  591.     }
  592.     exts = ext;
  593.     ext += num;
  594.     /* skip whitespace and store its name */
  595.     s2 = s = string + 6;
  596.     while (isspace(*s2))
  597.         s2++;
  598.     a = s2 - s;
  599.     ext->name = (char *) XpmMalloc(l - a - 6);
  600.     if (!ext->name) {
  601.         XpmFree(string);
  602.         ext->lines = NULL;
  603.         ext->nlines = 0;
  604.         XpmFreeExtensions(exts, num + 1);
  605.         return (XpmNoMemory);
  606.     }
  607.     strncpy(ext->name, s + a, l - a - 6);
  608.     XpmFree(string);
  609.     /* now store the related lines */
  610.     xpmNextString(data);
  611.     status = xpmGetString(data, &string, &l);
  612.     if (status != XpmSuccess) {
  613.         ext->lines = NULL;
  614.         ext->nlines = 0;
  615.         XpmFreeExtensions(exts, num + 1);
  616.         return (status);
  617.     }
  618.     ext->lines = (char **) XpmMalloc(sizeof(char *));
  619.     nlines = 0;
  620.     while ((notstart = strncmp("XPMEXT", string, 6))
  621.            && (notend = strncmp("XPMENDEXT", string, 9))) {
  622.         sp = (char **)
  623.         XpmRealloc(ext->lines, (nlines + 1) * sizeof(char *));
  624.         if (!sp) {
  625.         XpmFree(string);
  626.         ext->nlines = nlines;
  627.         XpmFreeExtensions(exts, num + 1);
  628.         return (XpmNoMemory);
  629.         }
  630.         ext->lines = sp;
  631.         ext->lines[nlines] = string;
  632.         nlines++;
  633.         xpmNextString(data);
  634.         status = xpmGetString(data, &string, &l);
  635.         if (status != XpmSuccess) {
  636.         ext->nlines = nlines;
  637.         XpmFreeExtensions(exts, num + 1);
  638.         return (status);
  639.         }
  640.     }
  641.     if (!nlines) {
  642.         XpmFree(ext->lines);
  643.         ext->lines = NULL;
  644.     }
  645.     ext->nlines = nlines;
  646.     num++;
  647.     }
  648.     if (!num) {
  649.     XpmFree(string);
  650.     XpmFree(exts);
  651.     exts = NULL;
  652.     } else if (!notend)
  653.     XpmFree(string);
  654.     *nextensions = num;
  655.     *extensions = exts;
  656.     return (XpmSuccess);
  657. }
  658.